Support OpenAPI 3.1: resolve array-valued type (unions & nullable) (#375) - #376
Merged
jemacineiras merged 4 commits intoJul 23, 2026
Merged
Conversation
…able)
OpenAPI 3.1 / JSON Schema 2020-12 allows a schema's `type` to be an array,
e.g. type: ["string", "null"] (the nullable idiom that replaces 3.0's
`nullable: true`, and general unions). ApiTool.getType() read the type via
JsonNode.textValue(), which returns null for an array node and collapsed to
"", so every isObject/isArray/isString/isNumber/isDateTime predicate and
MapperUtil.getSimpleType failed -> the generator silently produced wrong or
empty types for those fields.
- ApiTool.getType: when `type` is an array, resolve to the first non-"null"
entry ("null" only marks the type as nullable). All the isX predicates and
getSimpleType then work unchanged for union/nullable types.
- MapperUtil.processNumber: read the type via ApiTool.getType instead of
schema.get("type").asText(), so array-valued numeric types resolve.
- Add TypeConstants.NULL.
- Add a 3.1.0 regression fixture (testOpenApi31Types) covering ["string",
"null"], ["integer","null"], ["number","null"]+double, int64 and a plain
array, with golden assets.
Bumps version 6.3.2 -> 6.4.0 across the engine, maven and gradle modules.
Non-array `type` handling is unchanged, so existing 3.0 specs are unaffected.
Follow-up 3.1 items (examples array, $ref siblings, webhooks) tracked in sngular#375.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Not up to standards ⛔🔴 Issues
|
| Category | Results |
|---|---|
| CodeStyle | 2 minor |
🟢 Metrics 0 complexity · 0 duplication
Metric Results Complexity 0 Duplication 0
NEW Get contextual insights on your PRs based on Codacy's metrics, along with PR and Jira context, without leaving GitHub. Enable AI reviewer
TIP This summary will be updated as you push new changes.
jemacineiras
self-requested a review
July 22, 2026 16:33
jemacineiras
previously approved these changes
Jul 22, 2026
jemacineiras
previously approved these changes
Jul 22, 2026
Gradle 9.x plugin validation fails the build when a task type is neither @CacheableTask nor @DisableCachingByDefault. OpenApiTask and AsyncApiTask are code generators whose spec-file inputs are not declared as cacheable inputs, so caching is disabled explicitly with a documented reason. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
jemacineiras
approved these changes
Jul 23, 2026
jemacineiras
pushed a commit
that referenced
this pull request
Jul 23, 2026
* Fix #375: support OpenAPI 3.1 array-valued type (unions & nullable) OpenAPI 3.1 / JSON Schema 2020-12 allows a schema's `type` to be an array, e.g. type: ["string", "null"] (the nullable idiom that replaces 3.0's `nullable: true`, and general unions). ApiTool.getType() read the type via JsonNode.textValue(), which returns null for an array node and collapsed to "", so every isObject/isArray/isString/isNumber/isDateTime predicate and MapperUtil.getSimpleType failed -> the generator silently produced wrong or empty types for those fields. - ApiTool.getType: when `type` is an array, resolve to the first non-"null" entry ("null" only marks the type as nullable). All the isX predicates and getSimpleType then work unchanged for union/nullable types. - MapperUtil.processNumber: read the type via ApiTool.getType instead of schema.get("type").asText(), so array-valued numeric types resolve. - Add TypeConstants.NULL. - Add a 3.1.0 regression fixture (testOpenApi31Types) covering ["string", "null"], ["integer","null"], ["number","null"]+double, int64 and a plain array, with golden assets. Bumps version 6.3.2 -> 6.4.0 across the engine, maven and gradle modules. Non-array `type` handling is unchanged, so existing 3.0 specs are unaffected. Follow-up 3.1 items (examples array, $ref siblings, webhooks) tracked in #375. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * Support OpenAPI 3.1 top-level webhooks (#375) OpenAPI 3.1 adds a top-level `webhooks` object: a map of named Path Item Objects describing out-of-band requests. The generator only iterated `paths`, so webhooks were ignored - no handler interface, no payload models. Add OpenApiUtil.mergeWebhooksIntoPaths, invoked right after parsing in OpenApiGenerator.processFile. Each webhook (keyed by name) is merged into `paths` under a "/"-prefixed key so the existing pipeline generates a handler interface for its operations and the request/response payload models. `paths` is created if the contract has none, and existing `paths` entries are never overwritten. Adds a 3.1.0 webhooks-only regression fixture (testWebhooks) with golden assets (NewPetApi + PetDTO). Stacked on the 6.4.0 array-type work (#376); both are part of the 3.1 support milestone, so the version stays 6.4.0. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * Default tags on tag-less webhook operations to avoid NPE Webhook operations normally omit `tags`, but createOperation requires one (Objects.requireNonNull), so a realistic tag-less webhook crashed with a NullPointerException in the default by-url grouping mode - and was silently skipped in tag-grouping mode. The prior test masked this by giving the webhook explicit tags. mergeWebhooksIntoPaths now defaults each webhook operation's tags to the webhook name when absent/empty, and skips blank keys (guards the pathUrl.split()[1] grouping). The testWebhooks fixture is updated to the realistic tag-less shape to cover this. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * Harden 3.1 type/webhook handling and add edge-case tests getType: extract array-type resolution into a helper. A genuine multi-type union (e.g. ["string","integer"]) now logs a warning and uses the first concrete type; a "null"-only/empty type array falls back to `object` instead of an empty type string. Adds edge-case regression fixtures: - testOpenApi31Union: union -> String (+warning), ["null"] -> Object - testWebhookPathCollision: webhook name colliding with an existing path is dropped (existing paths take precedence). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * Annotate Gradle tasks with @DisableCachingByDefault Gradle 9.x plugin validation fails the build when a task type is neither @CacheableTask nor @DisableCachingByDefault. OpenApiTask and AsyncApiTask are code generators whose spec-file inputs are not declared as cacheable inputs, so caching is disabled explicitly with a documented reason. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix: update thollander/actions-comment-pull-request to v2 (Node 20 deprecated) --------- Co-authored-by: joseegarcia <jose.garcia@disashop.com> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
First step of #375 (Support OpenAPI 3.1.x): make the load-bearing 3.1 schema construct —
an array-valued
type— generate correctly.Root cause
OpenAPI 3.1 / JSON Schema 2020-12 lets a schema's
typebe an array, e.g.type: ["string", "null"](the nullable idiom that replaces 3.0'snullable: true, plus generalunions).
ApiTool.getType()read the type viaJsonNode.textValue(), which returnsnullfor anarray node and collapsed to
"". As a result everyisObject/isArray/isString/isNumber/isDateTimepredicate and
MapperUtil.getSimpleTypefailed, and the generator silently produced wrong/emptyJava types for those properties. It reproduces on any 3.1 spec using array types.
Change
ApiTool.getType— whentypeis an array, resolve to the first non-"null"entry(
"null"only marks nullability). EveryisXpredicate andgetSimpleTypethen work unchangedfor union/nullable types. Scalar
typehandling is untouched.MapperUtil.processNumber— read the type viaApiTool.getTypeinstead ofschema.get("type").asText(), so array-valued numeric types resolve.TypeConstants.NULL.Tests
New
3.1.0fixturetestOpenApi31Typeswith golden assets, exercising:typenickname["string", "null"]Stringage["integer", "null"]Integerscore["number", "null"]+format: doubleDoubleloginCountinteger+format: int64LongtagsarrayofstringList<String>OpenApiGeneratorTest: Tests run: 45, Failures: 0, Errors: 0;checkstyle:checkBUILD SUCCESS.Before the fix the array-typed properties collapsed to no/incorrect type.
Versioning
Bumps
6.3.2→6.4.0(minor, new feature) acrossmultiapi-engine,scs-multiapi-maven-plugin(own version + engine dependency) andscs-multiapi-gradle-plugin.Scope / follow-up
Focused on the one construct that makes 3.1 schemas actually generate. Remaining 3.1 checklist
items — JSON Schema
examplesarray,$refwith sibling keywords, top-levelwebhooks— arelower impact and tracked in #375 for follow-up PRs. Existing 3.0 specs are unaffected (non-array
typepath is unchanged).Closes the first checklist items of #375.
🤖 Generated with Claude Code